<!doctype html>
<html lang="zh-CN">
<head>
  <base href="https://www.nodeapp.cn/esm.html" />
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>ECMAScript 模块 | Node.js 中文文档 | Node.js 中文网</title>
  <meta name="description" content="Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型，使其轻量又高效。Node.js 的包管理器 npm，是全球最大的开源库生态系统。">
  <link rel="stylesheet" href="assets/style.css">
  <link rel="stylesheet" href="assets/sh.css">
  <link rel="canonical" href="https://www.nodeapp.cn/esm.html">
  <link rel="apple-touch-icon" href="apple-touch-icon.png">
  <link rel="icon" sizes="32x32" type="image/png" href="favicon.png">
  
  <script>
var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?acdf78480f7f8f2b23b812565a5868e0";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script>

</head>
<body class="alt apidoc" id="api-section-esm">
  <div id="content" class="clearfix">
    <div id="column1" data-id="esm" class="interior">
      <header>
        <h1>Node.js v8.x 中文文档</h1>
        <hr>
      </header>

      <div id="toc">
        <h2>目录</h2>
        <ul>
<li><span class="stability_1"><a href="#esm_ecmascript_modules">ECMAScript模块</a></span><ul>
<li><span class="stability_undefined"><a href="#esm_enabling">启用</a></span></li>
<li><span class="stability_undefined"><a href="#esm_features">特性</a></span><ul>
<li><span class="stability_undefined"><a href="#esm_supported">Supported</a></span></li>
<li><span class="stability_undefined"><a href="#esm_unsupported">Unsupported</a></span></li>
</ul>
</li>
<li><span class="stability_undefined"><a href="#esm_notable_differences_between_import_and_require"> <code>import</code> 与 <code>require</code> 之间的显著差异</a></span><ul>
<li><span class="stability_undefined"><a href="#esm_no_node_path">No NODE_PATH</a></span></li>
<li><span class="stability_undefined"><a href="#esm_no_require_extensions">No <code>require.extensions</code></a></span></li>
<li><span class="stability_undefined"><a href="#esm_no_require_cache">No <code>require.cache</code></a></span></li>
<li><span class="stability_undefined"><a href="#esm_url_based_paths">URL based paths</a></span></li>
</ul>
</li>
<li><span class="stability_undefined"><a href="#esm_interop_with_existing_modules">与现有模块的交互</a></span></li>
<li><span class="stability_undefined"><a href="#esm_loader_hooks">Loader hooks</a></span><ul>
<li><span class="stability_undefined"><a href="#esm_resolve_hook">Resolve hook</a></span></li>
<li><span class="stability_undefined"><a href="#esm_dynamic_instantiate_hook">Dynamic instantiate hook</a></span></li>
</ul>
</li>
</ul>
</li>
</ul>

      </div>
<div id="apicontent">
        <h1>ECMAScript模块<span><a class="mark" href="#esm_ecmascript_modules" id="esm_ecmascript_modules">#</a></span></h1>
<!--introduced_in=v8.5.0-->
<div class="api_stability api_stability_1"><a href="documentation.html#documentation_stability_index">稳定性: 1</a> - 试验的</div><!--name=esm-->
<p>Node.js包含了对基于 [Node.js EP for ES Modules] 的ES模块的支持。</p>
<p>不是所有EP的功能都是完整的并且将随着VM的支持和实现而就绪的。有问题的地方正在被修改完善。</p>
<h2>启用<span><a class="mark" href="#esm_enabling" id="esm_enabling">#</a></span></h2>
<!-- type=misc -->
<p><code>--experimental-modules</code> 标志可用于启用加载ES模块的功能。</p>
<p>一旦被设置启用，以 <code>.mjs</code> 为后缀的文件将能够作为ES模块加载。</p>
<pre><code class="lang-sh">node --experimental-modules my-app.mjs
</code></pre>
<h2>特性<span><a class="mark" href="#esm_features" id="esm_features">#</a></span></h2>
<!-- type=misc -->
<h3>Supported<span><a class="mark" href="#esm_supported" id="esm_supported">#</a></span></h3>
<p>只有在程序主入口添加CLI参数可以成为ES模块的入口点。在未来 <code>import()</code> 可以在程序运行时创建ES模块入口点。</p>
<h3>Unsupported<span><a class="mark" href="#esm_unsupported" id="esm_unsupported">#</a></span></h3>
<table>
<thead>
<tr>
<th>特性</th>
<th>原因</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>require(&apos;./foo.mjs&apos;)</code></td>
<td>ES模块具有不同的加载方式，使用 <code>import()</code>语言标准</td>
</tr>
<tr>
<td><code>import()</code></td>
<td>等待在Node.js中使用更加新的V8版本</td>
</tr>
<tr>
<td><code>import.meta</code></td>
<td>等待V8实现</td>
</tr>
</tbody>
</table>
<h2> <code>import</code> 与 <code>require</code> 之间的显著差异<span><a class="mark" href="#esm_notable_differences_between_import_and_require" id="esm_notable_differences_between_import_and_require">#</a></span></h2>
<h3>No NODE_PATH<span><a class="mark" href="#esm_no_node_path" id="esm_no_node_path">#</a></span></h3>
<p>根据 <code>NODE_PATH</code> 查询模块不是解析 <code>import</code> 的环节之一。如果想要怎么做，那么请使用符号链接。</p>
<h3>No <code>require.extensions</code><span><a class="mark" href="#esm_no_require_extensions" id="esm_no_require_extensions">#</a></span></h3>
<p><code>require.extensions</code> 没有被 <code>import</code> 使用。但是值得期望的是，加载器钩子可能在未来提供这个工作流流程。</p>
<h3>No <code>require.cache</code><span><a class="mark" href="#esm_no_require_cache" id="esm_no_require_cache">#</a></span></h3>
<p><code>require.cache</code> 没有被 <code>import</code> 使用。它有一个独立的缓存。</p>
<h3>URL based paths<span><a class="mark" href="#esm_url_based_paths" id="esm_url_based_paths">#</a></span></h3>
<p>ESM基于<a href="https://url.spec.whatwg.org/"><code>URL</code></a>语义来解析和缓存。这意味着那些包含特殊字符的文件名，如 <code>#</code> 和 <code>?</code> 需要进行转义。</p>
<p>如果使用 <code>import</code> 来解析一个拥有不同查询或片段的模块，该模块将被加载多次。</p>
<pre><code class="lang-js">import &apos;./foo?query=1&apos;; // loads ./foo with query of &quot;?query=1&quot;
import &apos;./foo?query=2&apos;; // loads ./foo with query of &quot;?query=2&quot;
</code></pre>
<p>直到现在，模块只能使用 <code>file:</code> 协议来加载。</p>
<h2>与现有模块的交互<span><a class="mark" href="#esm_interop_with_existing_modules" id="esm_interop_with_existing_modules">#</a></span></h2>
<p>所有CommonJS，JSON和C++模块都可以通过 <code>import</code> 来加载。</p>
<p>以这种方式加载的模块只加载一次，即使 <code>import</code> 语句中同一个模块的查询或片段字符串不同。</p>
<p>当通过 <code>import</code> 加载时，这些模块将提供一个 <code>default</code> 导出相当于完成计算后的 <code>module.exports</code> 。</p>
<pre><code class="lang-js">import fs from &apos;fs&apos;;
fs.readFile(&apos;./foo.txt&apos;, (err, body) =&gt; {
  if (err) {
    console.error(err);
  } else {
    console.log(body);
  }
});
</code></pre>
<h2>Loader hooks<span><a class="mark" href="#esm_loader_hooks" id="esm_loader_hooks">#</a></span></h2>
<!-- type=misc -->
<p>定制一个默认的模块解决方案，加载器钩子能通过提供给Node一个 --loader ./loader-name.mjs 参数来配置。</p>
<p>当此钩子被使用时，只支持ES模块加载，不支持任何的CommonJS模块加载。</p>
<h3>Resolve hook<span><a class="mark" href="#esm_resolve_hook" id="esm_resolve_hook">#</a></span></h3>
<p>The resolve hook returns the resolved file URL and module format for a
given module specifier and parent file URL:</p>
<pre><code class="lang-js">import url from &apos;url&apos;;

export async function resolve(specifier, parentModuleURL, defaultResolver) {
  return {
    url: new URL(specifier, parentModuleURL).href,
    format: &apos;esm&apos;
  };
}
</code></pre>
<p>The default NodeJS ES module resolution function is provided as a third
argument to the resolver for easy compatibility workflows.</p>
<p>In addition to returning the resolved file URL value, the resolve hook also
returns a <code>format</code> property specifying the module format of the resolved
module. This can be one of the following:</p>
<table>
<thead>
<tr>
<th><code>format</code></th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>&quot;esm&quot;</code></td>
<td>Load a standard JavaScript module</td>
</tr>
<tr>
<td><code>&quot;commonjs&quot;</code></td>
<td>Load a node-style CommonJS module</td>
</tr>
<tr>
<td><code>&quot;builtin&quot;</code></td>
<td>Load a node builtin CommonJS module</td>
</tr>
<tr>
<td><code>&quot;json&quot;</code></td>
<td>Load a JSON file</td>
</tr>
<tr>
<td><code>&quot;addon&quot;</code></td>
<td>Load a [C++ Addon][addons]</td>
</tr>
<tr>
<td><code>&quot;dynamic&quot;</code></td>
<td>Use a [dynamic instantiate hook][]</td>
</tr>
</tbody>
</table>
<p>For example, a dummy loader to load JavaScript restricted to browser resolution
rules with only JS file extension and Node builtin modules support could
be written:</p>
<pre><code class="lang-js">import url from &apos;url&apos;;
import path from &apos;path&apos;;
import process from &apos;process&apos;;
import Module from &apos;module&apos;;

const builtins = Module.builtinModules;
const JS_EXTENSIONS = new Set([&apos;.js&apos;, &apos;.mjs&apos;]);

export function resolve(specifier, parentModuleURL/*, defaultResolve */) {
  if (builtins.includes(specifier)) {
    return {
      url: specifier,
      format: &apos;builtin&apos;
    };
  }
  if (/^\.{0,2}[/]/.test(specifier) !== true &amp;&amp; !specifier.startsWith(&apos;file:&apos;)) {
    // For node_modules support:
    // return defaultResolve(specifier, parentModuleURL);
    throw new Error(
      `imports must begin with &apos;/&apos;, &apos;./&apos;, or &apos;../&apos;; &apos;${specifier}&apos; does not`);
  }
  const resolved = new url.URL(specifier, parentModuleURL);
  const ext = path.extname(resolved.pathname);
  if (!JS_EXTENSIONS.has(ext)) {
    throw new Error(
      `Cannot load file with non-JavaScript file extension ${ext}.`);
  }
  return {
    url: resolved.href,
    format: &apos;esm&apos;
  };
}
</code></pre>
<p>With this loader, running:</p>
<pre><code class="lang-console">NODE_OPTIONS=&apos;--experimental-modules --loader ./custom-loader.mjs&apos; node x.js
</code></pre>
<p>would load the module <code>x.js</code> as an ES module with relative resolution support
(with <code>node_modules</code> loading skipped in this example).</p>
<h3>Dynamic instantiate hook<span><a class="mark" href="#esm_dynamic_instantiate_hook" id="esm_dynamic_instantiate_hook">#</a></span></h3>
<p>To create a custom dynamic module that doesn&apos;t correspond to one of the
existing <code>format</code> interpretations, the <code>dynamicInstantiate</code> hook can be used.
This hook is called only for modules that return <code>format: &quot;dynamic&quot;</code> from
the <code>resolve</code> hook.</p>
<pre><code class="lang-js">export async function dynamicInstantiate(url) {
  return {
    exports: [&apos;customExportName&apos;],
    execute: (exports) =&gt; {
      // get and set functions provided for pre-allocated export names
      exports.customExportName.set(&apos;value&apos;);
    }
  };
}
</code></pre>
<p>With the list of module exports provided upfront, the <code>execute</code> function will
then be called at the exact point of module evaluation order for that module
in the import tree.
[Node.js EP for ES Modules]: <a href="https://github.com/nodejs/node-eps/blob/master/002-es-modules.md">https://github.com/nodejs/node-eps/blob/master/002-es-modules.md</a>
[addons]: addons.html
[dynamic instantiate hook]: #esm_dynamic_instantiate_hook</p>

      </div>
    </div>

    <div id="column2" class="interior">
      <div id="intro" class="interior">
        <a href="/" title="Go back to the home page">
          Node.js 中文文档 | Node.js 中文网
        </a>
      </div>
      
        <!-- [start-include:_toc.md] -->
<ul>
<li><a href="documentation.html">关于本文档</a></li>
<li><a href="synopsis.html">用法与例子</a></li>
</ul>
<div class="line"></div>

<ul>
<li><a href="assert.html">断言测试</a></li>
<li><a href="async_hooks.html">异步钩子（Async Hooks）</a></li>
<li><a href="buffer.html">缓存（Buffer）</a></li>
<li><a href="addons.html">C++ 插件</a></li>
<li><a href="n-api.html">C/C++ 插件 - N-API</a></li>
<li><a href="child_process.html">子进程</a></li>
<li><a href="cluster.html">集群（Cluster）</a></li>
<li><a href="cli.html">命令行参数</a></li>
<li><a href="console.html">控制台（Console）</a></li>
<li><a href="crypto.html">加密（Crypto）</a></li>
<li><a href="debugger.html">调试器</a></li>
<li><a href="deprecations.html">废弃的 API</a></li>
<li><a href="dns.html">DNS</a></li>
<li><a href="domain.html">域（Domain）</a></li>
<li><a href="esm.html">ECMAScript 模块</a></li>
<li><a href="errors.html">错误（Errors）</a></li>
<li><a href="events.html">事件（Events）</a></li>
<li><a href="fs.html">文件系统</a></li>
<li><a href="globals.html">全局对象（Globals）</a></li>
<li><a href="http.html">HTTP</a></li>
<li><a href="http2.html">HTTP/2</a></li>
<li><a href="https.html">HTTPS</a></li>
<li><a href="inspector.html">检查工具（Inspector）</a></li>
<li><a href="intl.html">国际化</a></li>
<li><a href="modules.html">模块（Modules）</a></li>
<li><a href="net.html">网络（Net）</a></li>
<li><a href="os.html">操作系统（OS）</a></li>
<li><a href="path.html">路径（Path）</a></li>
<li><a href="perf_hooks.html">性能钩子（Performance Hooks）</a></li>
<li><a href="process.html">进程</a></li>
<li><a href="punycode.html">Punycode</a></li>
<li><a href="querystring.html">查询字符串</a></li>
<li><a href="readline.html">逐行读取</a></li>
<li><a href="repl.html">交互式解释器（REPL）</a></li>
<li><a href="stream.html">流（Stream）</a></li>
<li><a href="string_decoder.html">字符串解码</a></li>
<li><a href="timers.html">定时器（Timers）</a></li>
<li><a href="tls.html">安全传输层（TLS/SSL）</a></li>
<li><a href="tracing.html">事件跟踪（Tracing）</a></li>
<li><a href="tty.html">TTY</a></li>
<li><a href="dgram.html">UDP / 数据报</a></li>
<li><a href="url.html">URL</a></li>
<li><a href="util.html">工具集</a></li>
<li><a href="v8.html">V8</a></li>
<li><a href="vm.html">虚拟机（VM）</a></li>
<li><a href="zlib.html">压缩（ZLIB）</a></li>
</ul>
<div class="line"></div>

<ul>
<li><a href="https://github.com/nodejs/node">GitHub 仓库和问题跟踪</a></li>
<li><a href="https://groups.google.com/group/nodejs">邮件列表</a></li>
</ul>
<!-- [end-include:_toc.md] -->

      
    </div>
  </div>
  <script src="assets/sh_main.js"></script>
  <script src="assets/sh_javascript.min.js"></script>
  <script>highlight(undefined, undefined, 'pre');</script>
</body>
</html>
